EXERCISE:
Apply PDP to the regression example of predicting bike rentals. Fit a random forest approximation for the prediction of bike rentals (cnt). Use the partial dependence plot to visualize the relationships the model learned. Use the slides shown in class as model.
QUESTION:
Analyse the influence of days since 2011, temperature, humidity and wind speed on the predicted bike counts.
library(dplyr)
library(plotly)
library(reshape2)
library(lubridate)
library(randomForestSRC)
library(modeldata)
library(caret)
library(dplyr)
library(ggplot2)
library(lime)
set.seed(1)
days <- read.csv("day.csv")
hour <- read.csv("hour.csv")
days$dteday <- as_date(days$dteday)
days_since <- select(days, workingday, holiday, temp, hum, windspeed, cnt)
days_since$days_since_2011 <- int_length(interval(ymd("2011-01-01"), days$dteday)) / (3600*24)
days_since$SUMMER <- ifelse(days$season == 3, 1, 0)
days_since$FALL <- ifelse(days$season == 4, 1, 0)
days_since$WINTER <- ifelse(days$season == 1, 1, 0)
days_since$MISTY <- ifelse(days$weathersit == 2, 1, 0)
days_since$RAIN <- ifelse(days$weathersit == 3 | days$weathersit == 4, 1, 0)
days_since$temp <- days_since$temp * 47 - 8
days_since$hum <- days_since$hum * 100
days_since$windspeed <- days_since$windspeed * 67
rf <- rfsrc(cnt~., data=days_since)
results <- select(days_since, days_since_2011, temp, hum, windspeed, cnt)
nr <- nrow(days_since)
for(c in names(results)[1:4])
{
for(i in 1:nr){
r <- days_since
r[[c]] <- days_since[[c]][i]
sal <- predict(rf, r)$predicted
results[[c]][i] <- (sum(sal) / nr)
}
}
results$D1=days_since$days_since_2011
results$T1=days_since$temp
results$H1=days_since$hum
results$W1=days_since$windspeed
library(pdp)
library(vip)
p1=ggplot(results,aes(x=D1,y=days_since_2011))+geom_line()+geom_rug(alpha=0.1,sides='b')+labs(x='Days_since_2011')
p2=ggplot(results,aes(x=T1,y=temp))+geom_line()+geom_rug(alpha=0.1,sides='b')+labs(x='Temperature')
p3=ggplot(results,aes(x=H1,y=hum))+geom_line()+geom_rug(alpha=0.1,sides='b')+labs(x='Humidity')
p4=ggplot(results,aes(x=W1,y=windspeed))+geom_rug(alpha=0.1,sides='b')+geom_line()+labs(x='Windspeed')
subplot(p1,p2,p3,p4, shareX = FALSE, titleX = TRUE)
NA
QUESTION:
Analyse the influence of days since 2011, temperature, humidity and wind speed on the predicted bike counts.
In first place, in the PDP for *days_since_2011** we can observe that there is two differenciable parts, the first one in the first year (days 0-360 aproximately) and the second year (days 361-730 aproximately). In the first year we can observe an almost linear increase in the bicycle rentals until a few days before the day 100 that goes from 2750 to aproximately 3000. After that there is a exponencial increase that goes until the day 110 aprox fllowed by an stable period of time with a low decreasement around the day 360. In the second year we can observe a huge exponencial increasemenet that finishes the day 420, followed by a low linear increase until the day 650 and finally a decrease until the last day. In general, we can say that the bike rentals increases with the time but the last days has been a little decrease.
Secondly, in the temp plot we can observe the bike rentals depending on the temperature. At first, the bike rentals do not change in the gap including the temperatures between negative and 4 degrees and stay around 3150. In the gap between 4 and 22 degrees we can observe a huge increase in the bike renatls that gets to the maximum of almost 5200 rentals. Finally, the tempertaures above 22 degrees have less rentals each time the temperature increases until the maximum temperature around 32 degrees that have 4750 rentals. In general we can say that the renatls increases when the temperature inccreases, but when the temperatures are really high the rentals decreases slowly.
In third place, in the plot of humidity we can see that the humidities between 0 and 62,5% has very similar rentals above 4625 and after that it is produced a huge decreasement of the rentals until the humdity close to the 100% that gets to 3500 rentals. In general, the rentals decreases with the humidity except when the humidity has high values (62.5-100) and the rest of values of humidity the bike rantals are the highest but the same for all this values.
In the end, in the windspeed plot we can see that the rentals clearly decreases from 0 to 25 km/h that goes from 4625 rentals to 4000 rentals. After that, in the gap between 25 to 35 km/h we can see that the rentals do not change significantly and always is in the lowest number of rentals. In general, the rentals decrease with the wind speed.
EXERCISE:
Generate a 2D Partial Dependency Plot with humidity and temperature to predict the number of bikes rented depending of those parameters.
BE CAREFUL: due to the size, extract a set of random samples from the BBDD before generating the the data for the Partial Dependency Plot.
Show the density distribution of both input features with the 2D plot as shown in the class slides.
TIP: Use geom_tile() to generate the 2D plot. Set width and height to avoid holes.
library(tictoc)
set.seed(1)
sampled <- sample_n(days_since, 40)
temp <- sampled$temp
hum <- sampled$hum
th <- inner_join(data.frame(temp),data.frame(hum), by=character())
th$p <- 0
for(i in 1:nrow(th)){
r <- days_since
r[["temp"]] <- th[["temp"]][i]
r[["hum"]] <- th[["hum"]][i]
sal <- predict(rf, r)$predicted
th[["p"]][i] <- sum(sal) / nr
}
QUESTION:
Interpret the results.
EXERCISE:
Apply the previous concepts to predict the price of a house from the database kc_house_data.csv. In this case, use again a random forest approximation for the prediction based on the features bedrooms, bathrooms, sqft_living, sqft_lot, floors and yr_built. Use the partial dependence plot to visualize the relationships the model learned.
BE CAREFUL: due to the size, extract a set of random samples from the BBDD before generating the data for the Partial Dependency Plot.
set.seed(15)
d <- read.csv("kc_house_data.csv")
sampled <- sample_n(d, 1000)
sampled <- select(sampled, bedrooms, bathrooms, sqft_living, sqft_lot, floors, yr_built, price)
rf <- rfsrc(price~., data=sampled)
results <- select(sampled, bedrooms, bathrooms, sqft_living, floors, price)
nr <- nrow(sampled)
for(c in names(results)[1:4])
{
for(i in 1:nr){
r <- sampled
r[[c]] <- sampled[[c]][i]
sal <- predict(rf, r)$predicted
results[[c]][i] <- sum(sal) / nr
}
}
QUESTION:
Analyse the influence of bedrooms, bathrooms, sqft_living and floors on the predicted price.
LS0tDQp0aXRsZTogIlhBSSAzOiBNb2RlbC1BZ25vc3RpYyBtZXRob2RzIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQpuYW1lczogQ2FybGVzIEN1YmVyb3MgU2F1cywgSm9yZ2UgU2FudGlhZ28gR29uesOhbGV6DQotLS0NCg0KIyMgRVhFUkNJU0U6DQoNCkFwcGx5IFBEUCB0byB0aGUgcmVncmVzc2lvbiBleGFtcGxlIG9mIHByZWRpY3RpbmcgYmlrZSByZW50YWxzLiBGaXQgYSByYW5kb20gZm9yZXN0IGFwcHJveGltYXRpb24gZm9yIHRoZSBwcmVkaWN0aW9uIG9mIGJpa2UgcmVudGFscyAoKipjbnQqKikuIFVzZSB0aGUgcGFydGlhbCBkZXBlbmRlbmNlIHBsb3QgdG8gdmlzdWFsaXplIHRoZSByZWxhdGlvbnNoaXBzIHRoZSBtb2RlbCBsZWFybmVkLiBVc2UgdGhlIHNsaWRlcyBzaG93biBpbiBjbGFzcyBhcyBtb2RlbC4gIA0KDQojIyBRVUVTVElPTjoNCg0KQW5hbHlzZSB0aGUgaW5mbHVlbmNlIG9mICoqZGF5cyBzaW5jZSAyMDExLCB0ZW1wZXJhdHVyZSwgaHVtaWRpdHkqKiBhbmQgKip3aW5kIHNwZWVkKiogb24gdGhlIHByZWRpY3RlZCBiaWtlIGNvdW50cy4NCg0KDQpgYGB7ciwgd2FybmluZz1GQUxTRSxtZXNzYWdlPUZBTFNFfQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkocGxvdGx5KQ0KbGlicmFyeShyZXNoYXBlMikNCmxpYnJhcnkobHVicmlkYXRlKQ0KbGlicmFyeShyYW5kb21Gb3Jlc3RTUkMpDQpsaWJyYXJ5KG1vZGVsZGF0YSkNCmxpYnJhcnkoY2FyZXQpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShsaW1lKQ0KDQpzZXQuc2VlZCgxKQ0KDQpkYXlzIDwtIHJlYWQuY3N2KCJkYXkuY3N2IikNCmhvdXIgPC0gcmVhZC5jc3YoImhvdXIuY3N2IikNCg0KZGF5cyRkdGVkYXkgPC0gYXNfZGF0ZShkYXlzJGR0ZWRheSkNCmRheXNfc2luY2UgPC0gc2VsZWN0KGRheXMsIHdvcmtpbmdkYXksIGhvbGlkYXksIHRlbXAsIGh1bSwgd2luZHNwZWVkLCBjbnQpDQpkYXlzX3NpbmNlJGRheXNfc2luY2VfMjAxMSA8LSBpbnRfbGVuZ3RoKGludGVydmFsKHltZCgiMjAxMS0wMS0wMSIpLCBkYXlzJGR0ZWRheSkpIC8gKDM2MDAqMjQpDQpkYXlzX3NpbmNlJFNVTU1FUiA8LSBpZmVsc2UoZGF5cyRzZWFzb24gPT0gMywgMSwgMCkNCmRheXNfc2luY2UkRkFMTCA8LSBpZmVsc2UoZGF5cyRzZWFzb24gPT0gNCwgMSwgMCkNCmRheXNfc2luY2UkV0lOVEVSIDwtIGlmZWxzZShkYXlzJHNlYXNvbiA9PSAxLCAxLCAwKQ0KZGF5c19zaW5jZSRNSVNUWSA8LSBpZmVsc2UoZGF5cyR3ZWF0aGVyc2l0ID09IDIsIDEsIDApDQpkYXlzX3NpbmNlJFJBSU4gPC0gaWZlbHNlKGRheXMkd2VhdGhlcnNpdCA9PSAzIHwgZGF5cyR3ZWF0aGVyc2l0ID09IDQsIDEsIDApDQpkYXlzX3NpbmNlJHRlbXAgPC0gZGF5c19zaW5jZSR0ZW1wICogNDcgLSA4DQpkYXlzX3NpbmNlJGh1bSA8LSBkYXlzX3NpbmNlJGh1bSAqIDEwMA0KZGF5c19zaW5jZSR3aW5kc3BlZWQgPC0gZGF5c19zaW5jZSR3aW5kc3BlZWQgKiA2Nw0KDQpyZiA8LSByZnNyYyhjbnR+LiwgZGF0YT1kYXlzX3NpbmNlKQ0KDQoNCnJlc3VsdHMgPC0gc2VsZWN0KGRheXNfc2luY2UsIGRheXNfc2luY2VfMjAxMSwgdGVtcCwgaHVtLCB3aW5kc3BlZWQsIGNudCkNCm5yIDwtIG5yb3coZGF5c19zaW5jZSkNCmZvcihjIGluIG5hbWVzKHJlc3VsdHMpWzE6NF0pDQp7DQogIGZvcihpIGluIDE6bnIpew0KICAgIHIgPC0gZGF5c19zaW5jZQ0KICAgIHJbW2NdXSA8LSBkYXlzX3NpbmNlW1tjXV1baV0NCiAgICBzYWwgPC0gcHJlZGljdChyZiwgcikkcHJlZGljdGVkDQogICAgcmVzdWx0c1tbY11dW2ldIDwtIChzdW0oc2FsKSAvIG5yKQ0KICB9DQp9DQoNCnJlc3VsdHMkRDE9ZGF5c19zaW5jZSRkYXlzX3NpbmNlXzIwMTENCnJlc3VsdHMkVDE9ZGF5c19zaW5jZSR0ZW1wDQpyZXN1bHRzJEgxPWRheXNfc2luY2UkaHVtDQpyZXN1bHRzJFcxPWRheXNfc2luY2Ukd2luZHNwZWVkDQoNCmxpYnJhcnkocGRwKQ0KbGlicmFyeSh2aXApDQoNCg0KDQpwMT1nZ3Bsb3QocmVzdWx0cyxhZXMoeD1EMSx5PWRheXNfc2luY2VfMjAxMSkpK2dlb21fbGluZSgpK2dlb21fcnVnKGFscGhhPTAuMSxzaWRlcz0nYicpK2xhYnMoeD0nRGF5c19zaW5jZV8yMDExJykNCnAyPWdncGxvdChyZXN1bHRzLGFlcyh4PVQxLHk9dGVtcCkpK2dlb21fbGluZSgpK2dlb21fcnVnKGFscGhhPTAuMSxzaWRlcz0nYicpK2xhYnMoeD0nVGVtcGVyYXR1cmUnKQ0KcDM9Z2dwbG90KHJlc3VsdHMsYWVzKHg9SDEseT1odW0pKStnZW9tX2xpbmUoKStnZW9tX3J1ZyhhbHBoYT0wLjEsc2lkZXM9J2InKStsYWJzKHg9J0h1bWlkaXR5JykNCnA0PWdncGxvdChyZXN1bHRzLGFlcyh4PVcxLHk9d2luZHNwZWVkKSkrZ2VvbV9ydWcoYWxwaGE9MC4xLHNpZGVzPSdiJykrZ2VvbV9saW5lKCkrbGFicyh4PSdXaW5kc3BlZWQnKQ0KDQpzdWJwbG90KHAxLHAyLHAzLHA0LCBzaGFyZVggPSBGQUxTRSwgdGl0bGVYID0gVFJVRSkNCg0KYGBgDQoNCiMjIFFVRVNUSU9OOg0KDQpBbmFseXNlIHRoZSBpbmZsdWVuY2Ugb2YgKipkYXlzIHNpbmNlIDIwMTEsIHRlbXBlcmF0dXJlLCBodW1pZGl0eSoqIGFuZCAqKndpbmQgc3BlZWQqKiBvbiB0aGUgcHJlZGljdGVkIGJpa2UgY291bnRzLg0KDQpJbiBmaXJzdCBwbGFjZSwgaW4gdGhlIFBEUCBmb3IgKmRheXNfc2luY2VfMjAxMSoqIHdlIGNhbiBvYnNlcnZlIHRoYXQgdGhlcmUgaXMgdHdvIGRpZmZlcmVuY2lhYmxlIHBhcnRzLCB0aGUgZmlyc3Qgb25lIGluIHRoZSBmaXJzdCB5ZWFyIChkYXlzIDAtMzYwIGFwcm94aW1hdGVseSkgYW5kIHRoZSBzZWNvbmQgeWVhciAoZGF5cyAzNjEtNzMwIGFwcm94aW1hdGVseSkuIA0KSW4gdGhlIGZpcnN0IHllYXIgd2UgY2FuIG9ic2VydmUgYW4gYWxtb3N0IGxpbmVhciBpbmNyZWFzZSBpbiB0aGUgYmljeWNsZSByZW50YWxzIHVudGlsIGEgZmV3IGRheXMgYmVmb3JlIHRoZSBkYXkgMTAwIHRoYXQgZ29lcyBmcm9tIDI3NTAgdG8gYXByb3hpbWF0ZWx5IDMwMDAuIEFmdGVyIHRoYXQgdGhlcmUgaXMgYSBleHBvbmVuY2lhbCBpbmNyZWFzZSB0aGF0IGdvZXMgdW50aWwgdGhlIGRheSAxMTAgYXByb3ggZmxsb3dlZCBieSBhbiBzdGFibGUgcGVyaW9kIG9mIHRpbWUgd2l0aCBhIGxvdyBkZWNyZWFzZW1lbnQgYXJvdW5kIHRoZSBkYXkgMzYwLg0KSW4gdGhlIHNlY29uZCB5ZWFyIHdlIGNhbiBvYnNlcnZlIGEgaHVnZSBleHBvbmVuY2lhbCBpbmNyZWFzZW1lbmV0IHRoYXQgZmluaXNoZXMgdGhlIGRheSA0MjAsIGZvbGxvd2VkIGJ5IGEgbG93IGxpbmVhciBpbmNyZWFzZSB1bnRpbCB0aGUgZGF5IDY1MCBhbmQgZmluYWxseSBhIGRlY3JlYXNlIHVudGlsIHRoZSBsYXN0IGRheS4NCkluIGdlbmVyYWwsIHdlIGNhbiBzYXkgdGhhdCB0aGUgYmlrZSByZW50YWxzIGluY3JlYXNlcyB3aXRoIHRoZSB0aW1lIGJ1dCB0aGUgbGFzdCBkYXlzIGhhcyBiZWVuIGEgbGl0dGxlIGRlY3JlYXNlLg0KDQpTZWNvbmRseSwgaW4gdGhlICoqdGVtcCoqIHBsb3Qgd2UgY2FuIG9ic2VydmUgdGhlIGJpa2UgcmVudGFscyBkZXBlbmRpbmcgb24gdGhlIHRlbXBlcmF0dXJlLiBBdCBmaXJzdCwgdGhlIGJpa2UgcmVudGFscyBkbyBub3QgY2hhbmdlIGluIHRoZSBnYXAgaW5jbHVkaW5nIHRoZSB0ZW1wZXJhdHVyZXMgYmV0d2VlbiBuZWdhdGl2ZSBhbmQgNCBkZWdyZWVzIGFuZCBzdGF5IGFyb3VuZCAzMTUwLiBJbiB0aGUgZ2FwIGJldHdlZW4gNCBhbmQgMjIgZGVncmVlcyB3ZSBjYW4gb2JzZXJ2ZSBhIGh1Z2UgaW5jcmVhc2UgaW4gdGhlIGJpa2UgcmVuYXRscyB0aGF0IGdldHMgdG8gdGhlIG1heGltdW0gb2YgYWxtb3N0IDUyMDAgcmVudGFscy4gRmluYWxseSwgdGhlIHRlbXBlcnRhdXJlcyBhYm92ZSAyMiBkZWdyZWVzIGhhdmUgbGVzcyByZW50YWxzIGVhY2ggdGltZSB0aGUgdGVtcGVyYXR1cmUgaW5jcmVhc2VzIHVudGlsIHRoZSBtYXhpbXVtIHRlbXBlcmF0dXJlIGFyb3VuZCAzMiBkZWdyZWVzIHRoYXQgaGF2ZSA0NzUwIHJlbnRhbHMuIEluIGdlbmVyYWwgd2UgY2FuIHNheSB0aGF0IHRoZSByZW5hdGxzIGluY3JlYXNlcyB3aGVuIHRoZSB0ZW1wZXJhdHVyZSBpbmNjcmVhc2VzLCBidXQgd2hlbiB0aGUgdGVtcGVyYXR1cmVzIGFyZSByZWFsbHkgaGlnaCB0aGUgcmVudGFscyBkZWNyZWFzZXMgc2xvd2x5Lg0KDQpJbiB0aGlyZCBwbGFjZSwgaW4gdGhlIHBsb3Qgb2YgKipodW1pZGl0eSoqIHdlIGNhbiBzZWUgdGhhdCB0aGUgaHVtaWRpdGllcyBiZXR3ZWVuIDAgYW5kIDYyLDUlIGhhcyB2ZXJ5IHNpbWlsYXIgcmVudGFscyBhYm92ZSA0NjI1IGFuZCBhZnRlciB0aGF0IGl0IGlzIHByb2R1Y2VkIGEgaHVnZSBkZWNyZWFzZW1lbnQgb2YgdGhlIHJlbnRhbHMgdW50aWwgdGhlIGh1bWRpdHkgY2xvc2UgdG8gdGhlIDEwMCUgdGhhdCBnZXRzIHRvIDM1MDAgcmVudGFscy4gSW4gZ2VuZXJhbCwgdGhlIHJlbnRhbHMgZGVjcmVhc2VzIHdpdGggdGhlIGh1bWlkaXR5IGV4Y2VwdCB3aGVuIHRoZSBodW1pZGl0eSBoYXMgaGlnaCB2YWx1ZXMgKDYyLjUtMTAwKSBhbmQgdGhlIHJlc3Qgb2YgdmFsdWVzIG9mIGh1bWlkaXR5IHRoZSBiaWtlIHJhbnRhbHMgYXJlIHRoZSBoaWdoZXN0IGJ1dCB0aGUgc2FtZSBmb3IgYWxsIHRoaXMgdmFsdWVzLg0KDQpJbiB0aGUgZW5kLCBpbiB0aGUgKip3aW5kc3BlZWQqKiBwbG90IHdlIGNhbiBzZWUgdGhhdCB0aGUgcmVudGFscyBjbGVhcmx5IGRlY3JlYXNlcyBmcm9tIDAgdG8gMjUga20vaCB0aGF0IGdvZXMgZnJvbSA0NjI1IHJlbnRhbHMgdG8gNDAwMCByZW50YWxzLiBBZnRlciB0aGF0LCBpbiB0aGUgZ2FwIGJldHdlZW4gMjUgdG8gMzUga20vaCB3ZSBjYW4gc2VlIHRoYXQgdGhlIHJlbnRhbHMgZG8gbm90IGNoYW5nZSBzaWduaWZpY2FudGx5IGFuZCBhbHdheXMgaXMgaW4gdGhlIGxvd2VzdCBudW1iZXIgb2YgcmVudGFscy4gSW4gZ2VuZXJhbCwgdGhlIHJlbnRhbHMgZGVjcmVhc2Ugd2l0aCB0aGUgd2luZCBzcGVlZC4NCg0KDQoNCg0KIyMgRVhFUkNJU0U6DQoNCkdlbmVyYXRlIGEgMkQgUGFydGlhbCBEZXBlbmRlbmN5IFBsb3Qgd2l0aCBodW1pZGl0eSBhbmQgdGVtcGVyYXR1cmUgdG8gcHJlZGljdCB0aGUgbnVtYmVyIG9mIGJpa2VzIHJlbnRlZCBkZXBlbmRpbmcgb2YgdGhvc2UgcGFyYW1ldGVycy4NCg0KQkUgQ0FSRUZVTDogZHVlIHRvIHRoZSBzaXplLCBleHRyYWN0IGEgc2V0IG9mIHJhbmRvbSBzYW1wbGVzIGZyb20gdGhlIEJCREQgYmVmb3JlIGdlbmVyYXRpbmcgdGhlIHRoZSBkYXRhIGZvciB0aGUgUGFydGlhbCBEZXBlbmRlbmN5IFBsb3QuIA0KDQpTaG93IHRoZSBkZW5zaXR5IGRpc3RyaWJ1dGlvbiBvZiBib3RoIGlucHV0IGZlYXR1cmVzIHdpdGggdGhlIDJEIHBsb3QgYXMgc2hvd24gaW4gdGhlIGNsYXNzIHNsaWRlcy4gDQoNClRJUDogVXNlIGdlb21fdGlsZSgpIHRvIGdlbmVyYXRlIHRoZSAyRCBwbG90LiBTZXQgd2lkdGggYW5kIGhlaWdodCB0byBhdm9pZCBob2xlcy4gDQoNCg0KDQoNCmBgYHtyLHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KHRpY3RvYykNCnNldC5zZWVkKDEpDQpzYW1wbGVkIDwtIHNhbXBsZV9uKGRheXNfc2luY2UsIDQwKQ0KdGVtcCA8LSBzYW1wbGVkJHRlbXANCmh1bSA8LSBzYW1wbGVkJGh1bQ0KdGggPC0gaW5uZXJfam9pbihkYXRhLmZyYW1lKHRlbXApLGRhdGEuZnJhbWUoaHVtKSwgYnk9Y2hhcmFjdGVyKCkpDQp0aCRwIDwtIDANCg0KZm9yKGkgaW4gMTpucm93KHRoKSl7DQogIHIgPC0gZGF5c19zaW5jZQ0KICByW1sidGVtcCJdXSA8LSB0aFtbInRlbXAiXV1baV0NCiAgcltbImh1bSJdXSA8LSB0aFtbImh1bSJdXVtpXQ0KICANCiAgc2FsIDwtIHByZWRpY3QocmYsIHIpJHByZWRpY3RlZA0KICB0aFtbInAiXV1baV0gPC0gc3VtKHNhbCkgLyBucg0KfQ0KDQoNCmBgYA0KDQojIyBRVUVTVElPTjoNCg0KSW50ZXJwcmV0IHRoZSByZXN1bHRzLg0KDQoNCg0KDQojIyBFWEVSQ0lTRToNCg0KQXBwbHkgdGhlIHByZXZpb3VzIGNvbmNlcHRzIHRvIHByZWRpY3QgdGhlICoqcHJpY2UqKiBvZiBhIGhvdXNlIGZyb20gdGhlIGRhdGFiYXNlICoqa2NfaG91c2VfZGF0YS5jc3YqKi4gSW4gdGhpcyBjYXNlLCB1c2UgYWdhaW4gYSByYW5kb20gZm9yZXN0IGFwcHJveGltYXRpb24gZm9yIHRoZSBwcmVkaWN0aW9uIGJhc2VkIG9uIHRoZSBmZWF0dXJlcyAqKmJlZHJvb21zKiosICoqYmF0aHJvb21zKiosICoqc3FmdF9saXZpbmcqKiwgKipzcWZ0X2xvdCoqLCAqKmZsb29ycyoqIGFuZCAqKnlyX2J1aWx0KiouIA0KVXNlIHRoZSBwYXJ0aWFsIGRlcGVuZGVuY2UgcGxvdCB0byB2aXN1YWxpemUgdGhlIHJlbGF0aW9uc2hpcHMgdGhlIG1vZGVsIGxlYXJuZWQuDQoNCkJFIENBUkVGVUw6IGR1ZSB0byB0aGUgc2l6ZSwgZXh0cmFjdCBhIHNldCBvZiByYW5kb20gc2FtcGxlcyBmcm9tIHRoZSBCQkREIGJlZm9yZSBnZW5lcmF0aW5nIHRoZSBkYXRhIGZvciB0aGUgUGFydGlhbCBEZXBlbmRlbmN5IFBsb3QuIA0KDQoNCg0KDQpgYGB7ciwgfQ0Kc2V0LnNlZWQoMTUpDQpkIDwtIHJlYWQuY3N2KCJrY19ob3VzZV9kYXRhLmNzdiIpDQoNCnNhbXBsZWQgPC0gc2FtcGxlX24oZCwgMTAwMCkNCg0Kc2FtcGxlZCA8LSBzZWxlY3Qoc2FtcGxlZCwgYmVkcm9vbXMsIGJhdGhyb29tcywgc3FmdF9saXZpbmcsIHNxZnRfbG90LCBmbG9vcnMsIHlyX2J1aWx0LCBwcmljZSkNCg0KcmYgPC0gcmZzcmMocHJpY2V+LiwgZGF0YT1zYW1wbGVkKQ0KcmVzdWx0cyA8LSBzZWxlY3Qoc2FtcGxlZCwgYmVkcm9vbXMsIGJhdGhyb29tcywgc3FmdF9saXZpbmcsIGZsb29ycywgcHJpY2UpDQpuciA8LSBucm93KHNhbXBsZWQpDQpmb3IoYyBpbiBuYW1lcyhyZXN1bHRzKVsxOjRdKQ0Kew0KICBmb3IoaSBpbiAxOm5yKXsNCiAgICByIDwtIHNhbXBsZWQNCiAgICByW1tjXV0gPC0gc2FtcGxlZFtbY11dW2ldDQogICAgc2FsIDwtIHByZWRpY3QocmYsIHIpJHByZWRpY3RlZA0KICAgIHJlc3VsdHNbW2NdXVtpXSA8LSBzdW0oc2FsKSAvIG5yDQogIH0NCn0NCg0KYGBgDQoNCg0KIyMgUVVFU1RJT046DQoNCkFuYWx5c2UgdGhlIGluZmx1ZW5jZSBvZiAqKmJlZHJvb21zLCBiYXRocm9vbXMsIHNxZnRfbGl2aW5nKiogYW5kICoqZmxvb3JzKiogb24gdGhlIHByZWRpY3RlZCBwcmljZS4NCg0KDQoNCg==